/* internet - some stuff to make it easier to use
 * internet sockets and the like. */
#include "common.h"
#include "internet.h"

boolean internetIsDottedQuad(char *s)
/* Returns TRUE if it looks like s is a dotted quad. */
{
  int i;
  if (!isdigit(s[0]))
    return FALSE;
  for (i = 0; i < 3; ++i) {
    s = strchr(s, '.');
    if (s == NULL)
      return FALSE;
    s += 1;
    if (!isdigit(s[0]))
      return FALSE;
  }
  return TRUE;
}

bits32 internetHostIp(char *hostName)
/* Get IP v4 address (in host byte order) for hostName.
 * Warn and return 0 if there's a problem. */
{
#ifdef _STATIC
  // 127.0.0.1
  return 2130706433;
#else
  bits32 ret;
  if (internetIsDottedQuad(hostName)) {
    internetDottedQuadToIp(hostName, &ret);
  } else {
    /* getaddrinfo is thread-safe and widely supported */
    struct addrinfo hints, *res;
    struct in_addr addr;
    int err;

    zeroBytes(&hints, sizeof(hints));
    hints.ai_family = AF_INET;

    if ((err = getaddrinfo(hostName, NULL, &hints, &res)) != 0) {
      warn("getaddrinfo() error on hostName=%s: %s\n", hostName,
           gai_strerror(err));
      return 0;
    }

    addr = ((struct sockaddr_in *)(res->ai_addr))->sin_addr;

    ret = ntohl((uint32_t)addr.s_addr);

    freeaddrinfo(res);
  }
  return ret;
#endif
}

boolean internetFillInAddress(char *hostName, int port,
                              struct sockaddr_in *address)
/* Fill in address. Return FALSE if can't.  */
{
  ZeroVar(address);
  address->sin_family = AF_INET;
  address->sin_port = htons(port);
  if (hostName == NULL)
    address->sin_addr.s_addr = INADDR_ANY;
  else {
    if ((address->sin_addr.s_addr = htonl(internetHostIp(hostName))) == 0)
      return FALSE;
  }
  return TRUE;
}

boolean internetIpToDottedQuad(bits32 ip, char dottedQuad[17])
/* Convert IP4 address in host byte order to dotted quad
 * notation.  Warn and return FALSE if there's a
 * problem. */
{
#ifndef __CYGWIN32__
  struct in_addr ia;
  zeroBytes(dottedQuad, 17);
  ZeroVar(&ia);
  ia.s_addr = htonl(ip);
  if (inet_ntop(AF_INET, &ia, dottedQuad, 16) == NULL) {
    warn("conversion problem on 0x%x in internetIpToDottedQuad: %s", ip,
         strerror(errno));
    return FALSE;
  }
  return TRUE;
#else
  warn("Sorry, internetIpToDottedQuad not supported in Windows.");
  return FALSE;
#endif
}

boolean internetDottedQuadToIp(char *dottedQuad, bits32 *retIp)
/* Convert dotted quad format address to IP4 address in
 * host byte order.  Warn and return FALSE if there's a
 * problem. */
{
#ifndef __CYGWIN32__
  struct in_addr ia;
  if (inet_pton(AF_INET, dottedQuad, &ia) < 0) {
    warn("internetDottedQuadToIp problem on %s: %s", dottedQuad,
         strerror(errno));
    return FALSE;
  }
  *retIp = ntohl(ia.s_addr);
  return TRUE;
#else
  warn("Sorry, internetDottedQuadToIp not supported in Windows.");
  return FALSE;
#endif
}

void internetParseDottedQuad(char *dottedQuad, unsigned char quad[4])
/* Parse dotted quads into quad */
{
  char *s = dottedQuad;
  int i;
  if (!internetIsDottedQuad(s))
    errAbort("%s is not a dotted quad", s);
  for (i = 0; i < 4; ++i) {
    quad[i] = atoi(s);
    s = strchr(s, '.') + 1;
  }
}

void internetUnpackIp(bits32 packed, unsigned char unpacked[4])
/* Convert from 32 bit to 4-byte format with most significant
 * byte first. */
{
  int i;
  for (i = 3; i >= 0; --i) {
    unpacked[i] = (packed & 0xff);
    packed >>= 8;
  }
}

boolean internetIpInSubnet(unsigned char unpackedIp[4], unsigned char subnet[4])
/* Return true if unpacked IP address is in subnet. */
{
  int i;
  for (i = 0; i < 4; ++i) {
    unsigned char c = subnet[i];
    if (c == 255)
      return TRUE;
    if (c != unpackedIp[i])
      return FALSE;
  }
  return TRUE;
}
